///////////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2007-2009 ILOG, S.A.
//  All Rights Reserved.
//  The following is ILOG Source Code.  No modifications may be made to the  
//  ILOG Source Code.  Any usage of the ILOG Source Code is subject to 
//  the terms and conditions of the ILOG End User License Agreement   
//  applicable to this ILOG software product.
//
///////////////////////////////////////////////////////////////////////////////

package ilog.gauges.circular.renderers
{
  import flash.display.DisplayObject;
  import flash.display.Sprite;
  import flash.geom.Point;
  
  import ilog.gauges.gaugesClasses.circular.CircularValueRenderer;
  import ilog.utils.MathUtil;
  
  import mx.core.IFlexDisplayObject;
  
  [DefaultProperty("asset")]
  
  /**
   * The <code>CircularGaugeAsset</code> is a wrapper to include visual external 
   * assets into a circular gauge layout.
   *  
   * @mxml
   *  
   * <p>The <code>&lt;ilog:CircularGaugeAsset&gt;</code> tag inherits all the properties
   * of its parent classes and adds the following properties:</p>
   *  
   * <pre>
   * &lt;ilog:CircularGaugeAsset
   *   <strong>Properties</strong>
   *   asset="<i>Class; no default</i>"
   *   assetOriginX="50%"
   *   assetOriginY="50%"
   *   mode="decoration|rotation"
   *   offsetAngle="0"
   * /&gt;
   * </pre>
   *  
   * @see ilog.gauges.gaugesClasses.circular.CircularGaugeElement
   * @includeExample examples/CircularGaugeAssetExample.mxml -noswf
   */  
  public class CircularGaugeAsset extends CircularValueRenderer
  {
    /**
     *  Constructor
     */                  
    public function CircularGaugeAsset()
    {
      super();
    }

    /**
     *  @private
     */                
    private var _componentInstance:IFlexDisplayObject;

    /**
     *  @private
     */
    private var _asset:Class;
    
    /**
     *  @private
     */    
    private var _componentHandler:Sprite;

    private var _mode:String = "decoration";
    
    [Inspectable(category="Gauge", enumeration="decoration,rotation", defaultValue="decoration")]
    
    /**
     * Specifies the <code>value</code> to be used. Possible values are:
     * <ul>
     * <li><code>"decoration"</code> (default): The asset does not rotate even if a value is set</li>
     * <li><code>"rotation"</code>: The asset angle corresponds to the value angle returned by the scale.</li>
     * </ul>
     * 
     * @default "decoration"
     * 
     */
    public function get mode():String
    {
      return _mode;
    }
    
    /**
     *  @private
     */    
    public function set mode(value:String):void
    {
      _mode = value;
      invalidateDisplayList();
    }

    [Inspectable(category="Gauge")]
       
    /**
     * The class of the asset. Typically, you include SWF assets
     * with the <code>[Embed]</code> tag and pass the obtained class to this attribute.
     * The class must implement the <code>IFlexDisplayObject</code> interface,
     * which is the case of all assets embedded using the <code>[Embed]</code> tag and 
     * <code>ProgrammaticSkin</code>. 
     */                
    public function get asset():Class
    {
      return _asset;
    }

    /**
     *  @private
     */
    public function set asset(value:Class):void
    {
      if (_componentInstance != null) {
        _componentHandler.removeChild(_componentInstance as DisplayObject);
        // invalidate for commitProperties to re-create it
        _componentInstance = null;
      }
      _asset = value;
      invalidateProperties();
    }

    /**
     *  @private
     */
    private function getPercentAssetOriginX():Number
    {
      return isNaN(percentAssetOriginX) ? 0.5 : percentAssetOriginX / 100;
    }   

    /**
     *  @private
     */
    private function getPercentAssetOriginY():Number
    {
      return isNaN(percentAssetOriginY) ? 0.5 : percentAssetOriginY / 100;
    }

    /**
     *  @private
     */
    private function getAssetOrigin():Point
    {
      var px:Number = getPercentAssetOriginX();
      var py:Number = getPercentAssetOriginY();
      var ox:Number = ! isNaN(assetOriginX) ? assetOriginX : _componentInstance.measuredWidth * px;
      var oy:Number = ! isNaN(assetOriginY) ? assetOriginY : _componentInstance.measuredHeight * py;
      return new Point(ox, oy);
    }

    /**
     *  @private
     */    
    private var _percentAssetOriginX:Number = 50;

    [Inspectable(environment="none")]

    /**
     * Number that specifies the x-position of the rotating center as a percentage
     * of the asset width.
     *  
     * @default 50.
     */
    public function get percentAssetOriginX():Number
    {
      return _percentAssetOriginX;
    }

    /**
     *  @private
     */  
    public function set percentAssetOriginX(value:Number):void
    {
      _percentAssetOriginX = value;
      if (! isNaN(value))
        _assetOriginX = NaN;
      invalidateDisplayList();      
    }

    /**
     *  @private
     */  
    private var _percentAssetOriginY:Number = 50;

    [Inspectable(environment="none")]
    
    /**
     * Number that specifies the y-position of the rotating center as a percentage
     * of the asset height.
     *  
     * @default 50.
     */
    public function get percentAssetOriginY():Number
    {
      return _percentAssetOriginY;
    }

    /**
     *  @private
     */  
    public function set percentAssetOriginY(value:Number):void
    {
      _percentAssetOriginY = value;
     if (! isNaN(value))
      _assetOriginY = NaN;
     invalidateDisplayList();      
    }

    /**
     *  @private
     */  
    private var _assetOriginX:Number = NaN;

    [Bindable("assetOriginXChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentAssetOriginX")]

    /**
     * Number that specifies the x-position, in pixels, of the rotating center,
     * in the asset's coordinates.
     *
     * <p><strong>Note:</strong> You can specify a percentage value in the MXML
     * <code>assetOriginX</code> attribute, such as <code>assetOriginX="100%"</code>,
     * but you cannot use a percentage value in the <code>assetOriginX</code>
     * property in ActionScript.
     * Use the <code>percentAssetOriginX</code> property instead.</p>
     * 
     * @default NaN
     */
    public function get assetOriginX():Number
    {
      return _assetOriginX;
    }

    /**
     *  @private
     */
    public function set assetOriginX(value:Number):void
    {
      _assetOriginX = value;
      if (! isNaN(value))
        _percentAssetOriginX = NaN;
      invalidateDisplayList();
    }

    /**
     *  @private
     */  
    private var _assetOriginY:Number = NaN;

    [Bindable("assetOriginYChanged")]
    [Inspectable(category="Gauge")]
    [PercentProxy("percentAssetOriginY")]

    /**
     * Number that specifies the y-position, in pixels, of the rotating center,
     * in the asset's coordinates.
     *
     * <p><strong>Note:</strong> You can specify a percentage value in the MXML
     * <code>assetOriginY</code> attribute, such as <code>assetOriginY="100%"</code>,
     * but you cannot use a percentage value in the <code>assetOriginY</code>
     * property in ActionScript.
     * Use the <code>percentAssetOriginY</code> property instead.</p>
     * 
     * @default NaN 
     */
    public function get assetOriginY():Number
    {
      return _assetOriginY;
    }

    /**
     *  @private
     */  
    public function set assetOriginY(value:Number):void
    {
      _assetOriginY = value;
      if (! isNaN(value))
        _percentAssetOriginY = NaN;      
      invalidateDisplayList();      
    }

    /**
     *  @private
     */  
    private var _offsetAngle:Number = 0;

    [Inspectable(category="Gauge")]

    /**
     * Number that specifies an offset angle, in degrees, for the rotation angle of the asset.
     * For example, a needle asset drawn horizontally to the right does not require an offset angle
     * and can use the <code>0</code> default value. A needle asset drawn vertically to 
     * the top requires you to set an offset angle of <code>-90</code>.
     * 
     * @default 0
     */
    public function get offsetAngle():Number
    {
      return _offsetAngle;
    }

    /**
     *  @private
     */  
    public function set offsetAngle(val:Number):void
    {
      _offsetAngle = val;
      invalidateDisplayList();
    }
 
    /**
     * @private
     */
    protected override function createChildren():void
    {
      if (_componentHandler == null) {
        _componentHandler = new Sprite();
        addChild(_componentHandler);
      }
    }    
    
    /**
     * @private
     */
    protected override function commitProperties():void
    {
      super.commitProperties();
      if (_asset != null && _componentInstance == null) {
        _componentInstance = new _asset();
        _componentHandler.addChild(_componentInstance as DisplayObject);
      }    
    }
      
    /**
     * Draws the asset at the right position and angle.
     * 
     * @param unscaledWidth Specifies the width, in pixels, of the component,
     * in the component's coordinates, regardless of the value of the
     * <code>scaleX</code> property of the component.
     *
     * @param unscaledHeight Specifies the height, in pixels, of the component,
     * in the component's coordinates, regardless of the value of the
     * <code>scaleY</code> property of the component. 
     */  
    protected override function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
    {
      super.updateDisplayList(unscaledWidth, unscaledHeight);
      var o:Point = getOrigin();
      var r:Number = getRadius();
      var px:Number = percentOriginX / 100;
      var py:Number = percentOriginY / 100;
      var pax:Number;
      var pay:Number;

      var w:Number;
      var h:Number;

      var mw:Number = _componentInstance.measuredWidth;
      var mh:Number = _componentInstance.measuredHeight;
      var a:Number;
      var v:Number;
      var afv:Number;
      _componentHandler.x = o.x;
      _componentHandler.y = o.y;

      if (isNaN(assetOriginX))
        pax = getPercentAssetOriginX();
      else
        pax = assetOriginX / mw;

      if (isNaN(assetOriginY))
        pay = getPercentAssetOriginY();
      else
        pay = assetOriginY / mh;

      if (radiusRelativeTo == "height") {
        h = r / (pay!=0 ? pay : 1);
        w = h * (mw / mh);
      } else if (radiusRelativeTo == "width") {
        w = r / (pax!=0 ? pax : 1);
        h = w * (mh / mw);
      }

      _componentInstance.setActualSize(w, h);
      _componentInstance.move(- pax * w, - pay * h);

      if (mode == "rotation") {
        if (isNaN(transitionAngle)) {  
          afv = MathUtil.toDegrees(scale.angleForValue(value));
        } else {
          afv = MathUtil.toDegrees(transitionAngle);
        }
      } else
        afv = 0;
      _componentHandler.rotation = 360 - (afv + offsetAngle);
    }
  }
}
